﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using UdpTransceiver;
using ParametersParser;
using System.Net;
using System.Threading;

namespace Exchange
{
    /// <summary>
    /// Виды параметров по направлению передачи - входные, выходные, комбинированные
    /// </summary>
    public enum ParameterDirection { input, output, inpout }
    /// <summary>
    /// Состояния коммуникатора
    /// </summary>
    public enum CommunicatorState { stoped, active }

    /// <summary>
    /// Класс-параметр обмена
    /// </summary>
    public class Parameter
    {
        /// <summary>
        /// Имя параметра
        /// </summary>
        public string Name;
        /// <summary>
        /// Значение параметра
        /// </summary>
        public string Value;
        /// <summary>
        /// Направление передачи значений параметра
        /// </summary>
        public ParameterDirection Direction;
        /// <summary>
        /// Время последнего обновления параметра
        /// </summary>
        public DateTime LastUpdateTime;
        /// <summary>
        /// Порядковый номер обновления параметра
        /// </summary>
        public Int64 UpdateCounter = 0;
        /// <summary>
        /// IP-адрес, с которого пришло последнее обновление
        /// </summary>
        public IPAddress LastIPReceiving;
        /// <summary>
        /// Порт, с которого пришло последнее обновление
        /// </summary>
        public int LastPortReceiving;
        /// <summary>
        /// Источник, с которого пришло последнее обновление
        /// </summary>
        public string LastSourceReceiver;
        /// <summary>
        /// Конструктор объекта- контейнера параметра
        /// </summary>
        /// <param name="Name">Имя параметра</param>
        /// <param name="Direction">Вид параметра по направлению передачи</param>
        public Parameter(string Name, ParameterDirection Direction)
        {
            this.Name = Name;
            this.Direction = Direction;
        }
    }
    /// <summary>
    /// Класс - коммуникатор. Обеспечивает обмен приложения с приложениями на удаленных узлах
    /// </summary>
    public class Communicator
    {
        /// <summary>
        /// Параметры соединения коммуникатора
        /// </summary>
        private static IPAddress m_localIP;
        private static int m_localPort;
        private static IPAddress m_remoteIP;
        private static int m_remotePort;
        private static UDPReceiver m_receiver;
        public CommunicatorState state;

        /// <summary>
        /// Поток для работы обменника коммуниктора
        /// </summary>
        Thread NetworkThread;
        /// <summary>
        /// Список параметров, которыми обменивается коммуникатор
        /// </summary>
        public List<Parameter> Parameters;

        private UDPTransmitter transmitter;
        private delegate void delTransmitter(UDPDatagram Datagram);

        /// <summary>
        /// Конструктор коммуникатора
        /// </summary>
        public Communicator()
        {
            m_localIP = IPAddress.Parse("127.0.0.1");
            m_localPort = 4001;
            m_remoteIP = IPAddress.Parse("127.0.0.1");
            m_remotePort = 4000;
            // Создаем приемник
            m_receiver = new UDPReceiver(m_localIP, m_localPort, m_remoteIP, m_remotePort);
            // Создаем пустой список параметров.
            Parameters = new List<Parameter>();
            // Создаем поток для работы обменника
            NetworkThread = new Thread(ListenNetwork);
        }
        /// <summary>
        /// Запускает в работу обменник
        /// </summary>
        public void Start()
        {
            state = CommunicatorState.active;
            NetworkThread.Start();
        }
        /// <summary>
        /// Останавливает работу обменника
        /// </summary>
        public void Stop()
        {
            state = CommunicatorState.stoped;
            NetworkThread.Abort();
        }
        /// <summary>
        /// Добавление параметра в список для обмена
        /// </summary>
        /// <param name="Name">Имя нового параметра</param>
        /// <param name="Direction">Вид параметра по направлению передачи</param>
        public void AddParameter(string Name, ParameterDirection Direction)
        {
            Parameters.Add(new Parameter(Name, Direction));
        }
        /// <summary>
        /// Удаление параметра из списка обменника по имени
        /// </summary>
        /// <param name="Name"></param>
        public void DeleteParameter(string Name)
        {

        }

        /// <summary>
        /// Получение значения параметра в строковом виде по его имени
        /// </summary>
        /// <param name="parameterName">Имя параметра, под которым он был зарегистрирован в коммуникаторе</param>
        /// <returns>Значение параметра в виде строки</returns>
        public string GetParameterValueByName(string parameterName)
        {
            return Parameters.Find(p => p.Name == parameterName).Value;
        }

        public void SetParameterValueByName(string parameterName, string Value)
        {
            Parameters.Find(p => p.Name == parameterName).Value = Value;
        }

        /// <summary>
        /// Собирает пакет для отправки к моделирующему узлу
        /// </summary>
        /// <returns></returns>
        private UDPDatagram CreateSendPackage()
        {
            UDPDatagram Result = new UDPDatagram();
            ModelMessage clsMessage = new ModelMessage();
            clsMessage.Header_T = "VHost";
            clsMessage.Header_S = DateTime.Now.ToString();

            HybridDictionary ParametersSet = new HybridDictionary();
            //HybridDictionary Entry = (HybridDictionary)GadgetsMessageQueue.Dequeue();

            foreach (Parameter Item in Parameters)
            {
                if (Item.Direction == ParameterDirection.output || Item.Direction == ParameterDirection.inpout)
                {
                    ParametersSet.Add(Item.Name, Item.Value);
                }
                clsMessage.Parameters = ParametersSet;

            }
            Result.PackageType = PackageTypes.SendData;
            Result.MessageBody = clsMessage.ToString();

            //else
            //{
            //Если сообщений нет отправляем пустой пакет Ready
            //    Result.PackageType = PackageTypes.Ready;
            //    Result.MessageBody = String.Empty;
            //}

            return Result;
        }

        private void TransmittionComplete(IAsyncResult result)
        {
            transmitter.Dispose();
        }

        /// <summary>
        /// Осуществляет обмен коммуникатора с удаленным узлом
        /// </summary>
        private void ListenNetwork()
        {
            while (state == CommunicatorState.active)
            {
                //Получаем датаграмму
                UDPDatagram currentDatagramm = m_receiver.ReceiveDatagram();
                //Преобразуем датаграмму в сообщение
                var currentMessage = new ModelMessage(currentDatagramm.MessageBody);

                //Console.WriteLine("{0} : Package of type {1} received.", DateTime.Now, currentDatagramm.PackageType);
                //Если пришел информационный пакет
                if (currentDatagramm.PackageType == PackageTypes.Info)
                {
                    // Перебираем все параметры из списка обменика
                    foreach (Parameter Item in Parameters)
                    {
                        // Если параметр входящий или двунаправленый
                        if (Item.Direction == ParameterDirection.input || Item.Direction == ParameterDirection.inpout)
                        {
                            // Пробуем выделить параметр с таким именем из входящего пакета
                            var paramValue = currentMessage.GetParameter(Item.Name);
                            // Если такой параметр есть в пакете обновляем атрибуты параметра в обменнике
                            if (paramValue != null)
                            {
                                Item.Value = paramValue.ToString();
                                Item.LastUpdateTime = DateTime.Now;
                                Item.UpdateCounter++;
                                Item.LastIPReceiving = m_remoteIP;
                                Item.LastPortReceiving = m_remotePort;
                            }
                        }

                    }
                }
                // Если принятый пакет - это признак готовности главной машины к приему (Ready)
                else if (currentDatagramm.PackageType == PackageTypes.Ready)
                {

                    //InitializeTransmitter();
                    UDPDatagram SendDatagram = CreateSendPackage();
                    delTransmitter TransmitterDelegate = new delTransmitter(transmitter.Send);
                    AsyncCallback TransmitterCallBack = new AsyncCallback(TransmittionComplete);
                    TransmitterDelegate.BeginInvoke(SendDatagram, TransmitterCallBack, null);

                }
            }
        }
    }
}
